<script lang="ts" setup>
import { ref, type PropType, onMounted, nextTick, watch } from "vue";

interface optionItem {
  lable: string;
  value: any;
  icon?: string;
  [key: string]: any;
}
const props = defineProps({
  options: {
    type: Array as PropType<optionItem[]>,
    default: () => [],
  },
  modelValue: {
    type: [String, Number, Boolean] as PropType<any>,
    default: 0,
  },
  isVertical: {
    type: Boolean,
    default: false,
  },
  isAequilate: {
    type: Boolean,
    default: false,
  },
});
const emits = defineEmits(["update:modelValue", "change"]);
const radioBodyDom = ref<HTMLElement>();
function select(item: optionItem) {
  if (item.value === props.modelValue) {
    return;
  }
  emits("update:modelValue", item.value);
  emits("change", item.value);
  nextTick(updateSliderPosition);
}

function updateSliderPosition(dom?: HTMLElement) {
  if (!radioBodyDom.value) {
    return;
  }
  let _dom;
  if (dom) {
    _dom = dom;
  } else {
    [...radioBodyDom.value.children].some((item) => {
      if (item instanceof HTMLElement) {
        const id = item.getAttribute("id");
        if (id === props.modelValue.toString()) {
          _dom = item;
          return true;
        }
      }
    });
  }
  if (!_dom) {
    return;
  }
  const itemRect = _dom.getBoundingClientRect();
  const radioBodyDomRect = radioBodyDom.value.getBoundingClientRect();
  const scollTop = radioBodyDom.value.scrollTop;
  const left = itemRect.left - radioBodyDomRect.left;
  const top = itemRect.top - radioBodyDomRect.top + scollTop;
  const width = itemRect.width;
  const height = itemRect.height;
  if (props.isVertical) {
    radioBodyDom.value.style.setProperty("--top", `${top}px`);
    radioBodyDom.value.style.setProperty("--height", `${height}px`);
  } else {
    radioBodyDom.value.style.setProperty("--left", `${left}px`);
    radioBodyDom.value.style.setProperty("--width", `${width}px`);
  }
  // 如果top超出了可视区域
  if (top < scollTop) {
    radioBodyDom.value.scrollTo({
      top: top - 30,
      behavior: "smooth",
    });
  } else if (top + height > scollTop + radioBodyDomRect.height) {
    radioBodyDom.value.scrollTo({
      top: top + height - radioBodyDomRect.height + 30,
      behavior: "smooth",
    });
  }
}
onMounted(() => {
  updateSliderPosition();
});
watch(
  () => props.options,
  () => {
    if (!props.options.length) return;
    nextTick(() => {
      updateSliderPosition();
    });
  }
);
watch(
  () => props.modelValue,
  () => {
    updateSliderPosition();
  }
);
</script>

<template>
  <div
    class="radioBody"
    :style="{
      flexDirection: props.isVertical ? 'column' : 'row',
    }"
    ref="radioBodyDom"
  >
    <template
      v-for="item in props.options"
      :key="item.value"
    >
      <div
        :style="{
          display: props.isVertical ? 'block' : 'inline-block',
          textAlign: !props.isVertical ? 'center' : 'left',
        }"
        class="item ripple"
        :id="item.value"
        @click="select(item)"
      >
        <slot
          name="option"
          v-if="$slots.option"
          :data="item"
        ></slot>
        <span v-else>
          {{ item.lable }}
        </span>
      </div>
    </template>
  </div>
</template>

<style lang="less" scoped>
.radioBody {
  position: relative;
  padding: 0.5em;
  z-index: 1;
  --left: 0.5em;
  --top: 0.5em;
  --width: calc(100% - 1em);
  --height: calc(100% - 1em);
  &::before {
    content: "";
    position: absolute;
    left: var(--left);
    top: var(--top);
    width: var(--width);
    height: var(--height);
    border-radius: 0.5em;
    box-shadow: inset 0 0 0.5em #ccc;
    transition: all 0.3s;
    z-index: -1;
  }
  .item {
    padding: 0.5em 1em;
    border-radius: 0.5em;
    transition: 0.3s;
    align-items: center;
    white-space: nowrap;
    white-space: pre;
    text-overflow: ellipsis;
    overflow: hidden;
    cursor: pointer;
    color: var(--color-primary);
    user-select: none;
    &:hover {
      background-color: var(--color-primary);
      color: var(--color-body);
    }
  }
}
</style>
